package com.sparrow.common.util;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit;

/**
 * 时间相关工具类
 *
 * @author liushengbao
 */
public class TimeUtil {

    // 日期时间格式化模式
    private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
    // 日期时间格式化器
    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT);
    // 一天的秒数
    private static final long ONE_DAY_SECONDS = 86400L;
    // 一天的毫秒数
    private static final long ONE_DAY_MILLIS = ONE_DAY_SECONDS * 1000;
    // 默认时区
    private static ZoneId ZONE_ID = ZoneId.systemDefault();

    public static void setZoneId(ZoneId zoneId) {
        ZONE_ID = zoneId;
    }

    /**
     * 获取系统当前时间戳（秒）,会在 2038-01-19 03:14:07 UTC 时 溢出
     *
     * @return 当前时间戳（秒）
     */
    public static int timestamp() {
        return (int) (System.currentTimeMillis() / 1000L);
    }

    public static long currentTimeMillis() {
        return System.currentTimeMillis();
    }

    /**
     * 获取当天凌晨的时间戳（秒）
     *
     * @return 当天凌晨的时间戳（秒）
     */
    public static int getMorningTime() {
        return (int) (getMorningTimeMillis() / 1000L);
    }

    /**
     * 获取当天凌晨的时间戳（毫秒）
     *
     * @return 当天凌晨的时间戳（毫秒）
     */
    public static long getMorningTimeMillis() {
        return getMorningTimeMillis(System.currentTimeMillis());
    }

    /**
     * 获取指定时间所在天凌晨的时间戳（毫秒）
     *
     * @param timeMillis 指定时间的毫秒数
     * @return 指定时间所在天凌晨的时间戳（毫秒）
     */
    public static long getMorningTimeMillis(long timeMillis) {
        LocalDate date = Instant.ofEpochMilli(timeMillis).atZone(ZONE_ID).toLocalDate();
        return date.atStartOfDay(ZONE_ID).toInstant().toEpochMilli();
    }

    /**
     * 获取指定时间对应的天数（基于时区安全）
     */
    public static int getDay(long timeMillis) {
        return (int) Instant.ofEpochMilli(timeMillis).atZone(ZONE_ID).toLocalDate().toEpochDay();
    }

    /**
     * 格式化时间戳为指定格式的字符串
     *
     * @param timestamp 时间戳（秒）
     * @return 格式化后的日期时间字符串
     */
    public static String format(int timestamp) {
        return format(timestamp, DATE_FORMAT);
    }

    public static String format(long timeMill) {
        return format(TimeUnit.MILLISECONDS.toSeconds(timeMill), DATE_FORMAT);
    }

    /**
     * 格式化时间戳为指定格式的字符串
     *
     * @param timestamp 时间戳（秒）
     * @param format    日期时间格式
     * @return 格式化后的日期时间字符串
     */
    public static String format(long timestamp, String format) {
        LocalDateTime dateTime = LocalDateTime.ofEpochSecond(timestamp, 0, ZONE_ID.getRules().getOffset(LocalDateTime.now()));
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
        return dateTime.format(formatter);
    }

    /**
     * 将日期时间字符串解析为时间戳（毫秒）
     *
     * @param timeString 日期时间字符串
     * @return 解析后的时间戳（毫秒）
     * @throws IllegalArgumentException 如果输入的日期时间字符串格式不正确
     */
    public static long parse(String timeString) {
        try {
            LocalDateTime dateTime = LocalDateTime.parse(timeString, DATE_TIME_FORMATTER);
            return dateTime.atZone(ZONE_ID).toInstant().toEpochMilli();
        } catch (Exception e) {
            throw new IllegalArgumentException("无法解析日期时间字符串: " + timeString, e);
        }
    }

    /**
     * 判断两个时间是否在同一天
     *
     * @param firstTimeMillis 第一个时间的毫秒数
     * @param secondTimeMillis 第二个时间的毫秒数
     * @return 如果两个时间在同一天返回 true，否则返回 false
     */
    public static boolean isSameDay(long firstTimeMillis, long secondTimeMillis) {
        LocalDate firstDate = Instant.ofEpochMilli(firstTimeMillis).atZone(ZONE_ID).toLocalDate();
        LocalDate secondDate = Instant.ofEpochMilli(secondTimeMillis).atZone(ZONE_ID).toLocalDate();
        return firstDate.isEqual(secondDate);
    }

    /**
     * 判断两个时间是否在同一个月
     *
     * @param srcTime 第一个时间的时间戳（秒）
     * @param tarTime 第二个时间的时间戳（秒）
     * @return 如果两个时间在同一个月返回 true，否则返回 false
     */
    public static boolean isSameMonth(int srcTime, int tarTime) {
        LocalDateTime srcDateTime = LocalDateTime.ofEpochSecond(srcTime, 0, ZONE_ID.getRules().getOffset(LocalDateTime.now()));
        LocalDateTime tarDateTime = LocalDateTime.ofEpochSecond(tarTime, 0, ZONE_ID.getRules().getOffset(LocalDateTime.now()));
        return srcDateTime.getYear() == tarDateTime.getYear() && srcDateTime.getMonth() == tarDateTime.getMonth();
    }

    /**
     * 获得当前日期是星期几
     *
     * @return 星期几（1 - 7），1 表示星期一，7 表示星期天
     */
    public static int getDayOfWeek() {
        LocalDate date = LocalDate.now(ZONE_ID);
        return date.getDayOfWeek().getValue();
    }

    /**
     * 获得当前日期是该月的第几天
     *
     * @return 该月的第几天（1 - 31）
     */
    public static int getDayOfMonth() {
        LocalDate date = LocalDate.now(ZONE_ID);
        return date.getDayOfMonth();
    }

    /**
     * 将时间偏移值转换成 x 天 x 时 x 分 x 秒 的字符串
     *
     * @param diffTime 时间偏移值（秒）
     * @return 格式化后的时间字符串
     */
    public static String toDayHourMinSec(int diffTime) {
        long days = diffTime / ONE_DAY_SECONDS;
        long remainingSeconds = diffTime % ONE_DAY_SECONDS;
        long hours = remainingSeconds / 3600;
        remainingSeconds %= 3600;
        long minutes = remainingSeconds / 60;
        long seconds = remainingSeconds % 60;
        return String.format("%d天%d小时%d分%d秒", days, hours, minutes, seconds);
    }

    public static void main(String[] args) {
        setZoneId(ZoneId.of("America/New_York"));
        System.out.println("ZONE=" + ZONE_ID);
        System.out.println("now=" + format(timestamp()));
        System.out.println("morning=" + format((int)(getMorningTimeMillis() / 1000)));
        System.out.println("sameDay=" + isSameDay(getMorningTimeMillis(), getMorningTimeMillis() + ONE_DAY_MILLIS));
    }
}